package com.ck.ips.service.impl;

import com.ck.common.model.DeviceGroup;
import com.ck.common.model.OperationResult;
import com.ck.core.constant.SymbolConstant;
import com.ck.core.constant.device.DeviceGroupConstant;
import com.ck.core.context.UserContextHolder;
import com.ck.core.util.TreeUtils;
import com.ck.ips.core.util.ListSortUtils;
import com.ck.ips.dao.DeviceGroupMapper;
import com.ck.ips.dao.RoleDeviceGroupMapper;
import com.ck.ips.model.vo.DeviceGroupTreeHierarchyVO;
import com.ck.ips.model.vo.TreeHierarchyVO;
import com.ck.ips.service.DeviceGroupService;
import com.ck.ips.service.DeviceService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.Collator;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 设备serviceImpl
 * @Author: hf
 * @Date: 2019/5/6 17:35
 * @Version: 1.0
 */
@Service(value = "deviceGroupService")
public class DeviceGroupServiceImpl implements DeviceGroupService {

    @Autowired
    private DeviceService deviceService;
    @Autowired
    private DeviceGroupMapper deviceGroupMapper;
    @Autowired
    private RoleDeviceGroupMapper roleDeviceGroupMapper;

    /**
     * 保存终端分组
     *
     * @param deviceGroup DeviceGroup类型对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OperationResult saveDeviceGroup(DeviceGroup deviceGroup) {
        deviceGroup.setDeviceGroupId(null);
        if (StringUtils.isBlank(deviceGroup.getGroupName())) {
            return OperationResult.newValidateFailOperationResult("分组名不能为空");
        }

        // 父id为空时，放到根节点下
        if (deviceGroup.getParentId() == null) {
            deviceGroup.setParentId(DeviceGroupConstant.ALL_DEVICE_GROUP_ID);
        }

        //检查分组名是否存在
        DeviceGroup nameDeviceGroup = deviceGroupMapper.getDeviceGroupByGroupName(deviceGroup.getGroupName());
        if (nameDeviceGroup != null) {
            return OperationResult.newValidateFailOperationResult("分组名已存在,不能重复");
        }
        deviceGroup.setUpdateTime(System.currentTimeMillis());

        //保存终端分组
        deviceGroupMapper.saveDeviceGroup(deviceGroup);

        return OperationResult.newSuccessTipsOperationResult("添加终端分组：" + deviceGroup.getGroupName());
    }


    /**
     * 更新终端分组
     *
     * @param deviceGroup DeviceGroup类型对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OperationResult updateDeviceGroup(DeviceGroup deviceGroup) {
        // 分组主键id
        Long deviceGroupId = deviceGroup.getDeviceGroupId();
        if (deviceGroupId == null) {
            return OperationResult.newValidateFailOperationResult("请选择要操作的分组");
        }
        if (StringUtils.isBlank(deviceGroup.getGroupName())) {
            return OperationResult.newValidateFailOperationResult("分组名不能为空");
        }
        if (DeviceGroupConstant.ALL_DEVICE_GROUP_ID.equals(deviceGroup.getDeviceGroupId()) || DeviceGroupConstant.NONE_DEVICE_GROUP_ID.equals(deviceGroup.getDeviceGroupId())) {
            return OperationResult.newValidateFailOperationResult("不能修改全部分组和默认分组");
        }
        DeviceGroup oldDeviceGroup = getDeviceGroup(deviceGroupId);
        if (oldDeviceGroup == null) {
            return OperationResult.newValidateFailOperationResult("分组信息不存在");
        }
        // 分组名
        String groupName = deviceGroup.getGroupName();
        DeviceGroup nameDeviceGroup = deviceGroupMapper.getDeviceGroupByGroupName(groupName);
        if (nameDeviceGroup != null) {
            // 如果已存在分组名相同且主键不同，则重复
            if (groupName.equals(nameDeviceGroup.getGroupName()) && !deviceGroupId.equals(nameDeviceGroup.getDeviceGroupId())) {
                return OperationResult.newValidateFailOperationResult("分组名已存在,不能重复");
            }
        }

        // 父id为空时，放到根节点下
        if (deviceGroup.getParentId() == null) {
            deviceGroup.setParentId(DeviceGroupConstant.ALL_DEVICE_GROUP_ID);
        }else {
            // 父id不为空时，判断是否能够设置该父id
            // 1、父id指向的分组必须存在，且必须在当前用户的可访问（操作）权限内
            // 2、指向的父id不能为自身或其子节点树，如：A >> B >> C 不能将 A分组移动到C分组下变成 B >> C >> A
            DeviceGroup destParentDeviceGroup = getDeviceGroup(deviceGroup.getParentId());
            if ( destParentDeviceGroup == null ){
                return OperationResult.newValidateFailOperationResult("父分组不存在，请刷新页面重试");
            }
            List<String> canAccessDeviceGroupId = findUserDeviceGroupId();
            if (canAccessDeviceGroupId == null || !canAccessDeviceGroupId.contains(deviceGroup.getParentId().toString())) {
                return OperationResult.newValidateFailOperationResult("父分组越权访问！");
            }
            List<DeviceGroup> destParentDeviceGroupTree = listDeviceGroupByParentId(deviceGroupId);
            if ( destParentDeviceGroupTree != null && destParentDeviceGroupTree.stream().map(DeviceGroup::getDeviceGroupId).collect(Collectors.toList()).contains(deviceGroup.getParentId())) {
                return OperationResult.newValidateFailOperationResult("父分组不能为自身及其子分组");
            }
        }

        // 修改保存记录
        deviceGroup.setUpdateTime(System.currentTimeMillis());
        deviceGroupMapper.updateDeviceGroup(deviceGroup);

        return OperationResult.newSuccessTipsOperationResult("更新终端分组：" +
                "源属性：【名称：" + oldDeviceGroup.getGroupName() + "，父分组："+oldDeviceGroup.getParentId()+"】，" +
                "新属性：【名称："+ groupName +"】，父分组：" + deviceGroup.getParentId());
    }

    /**
     * 获取终端分组
     *
     * @param deviceGroupId 终端分组ID
     */
    @Override
    public DeviceGroup getDeviceGroup(Long deviceGroupId) {
        return deviceGroupMapper.getDeviceGroupById(deviceGroupId);
    }

    /**
     * 删除终端分组
     *
     * @param deviceGroupId 终端分组ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OperationResult deleteDeviceGroup(Long deviceGroupId) {
        if (deviceGroupId == null) {
            return OperationResult.newValidateFailOperationResult("分组id不能为空");
        }
        if (DeviceGroupConstant.ALL_DEVICE_GROUP_ID.equals(deviceGroupId) || DeviceGroupConstant.NONE_DEVICE_GROUP_ID.equals(deviceGroupId)) {
            return OperationResult.newValidateFailOperationResult("不能删除全部分组和默认分组");
        }
        List<DeviceGroup> deviceChildGroupList = this.listDeviceGroupByParentId(deviceGroupId);
        if (!deviceChildGroupList.isEmpty()) {
            return OperationResult.newValidateFailOperationResult("删除终端分组失败,终端分组下有子分组");
        }
        DeviceGroup oldDeviceGroup = this.getDeviceGroup(deviceGroupId);
        if (oldDeviceGroup == null) {
            return OperationResult.newValidateFailOperationResult("分组信息不存在");
        }
        // 将所属终端的分组置为未分组
        deviceService.changeDeviceGroupToAnother(deviceGroupId, DeviceGroupConstant.NONE_DEVICE_GROUP_ID);

        //删除终端分组
        deviceGroupMapper.deleteDeviceGroupById(deviceGroupId);

        // 删除角色关联分组
        roleDeviceGroupMapper.delByDeviceGroupId(deviceGroupId);

        return OperationResult.newSuccessTipsOperationResult("删除终端分组：" + oldDeviceGroup.getGroupName());
    }

    /**
     * 通过父ID获取终端分组列表
     *
     * @param parentId 父ID
     * @return List<TbDeviceGroup>
     */
    @Override
    public List<DeviceGroup> listDeviceGroupByParentId(Long parentId) {
        return deviceGroupMapper.listDeviceGroupByParentId(parentId);
    }


    /**
     * 所有终端树形分组列表
     *
     * @return List<DeviceGroupTreeListDTO>
     * @param resolvingPower  处理分辨率是为了在选点时，设备数量能够跟设备查询列表匹配上
     */
    @Override
    public List listAllDeviceGroupTree(String resolvingPower) {

        // 分辨率(拼接多个)
        List<String> resolvingPowers = new ArrayList<>();
        if ( StringUtils.isNotBlank(resolvingPower) ) {
            resolvingPowers = Arrays.asList(resolvingPower.split(SymbolConstant.COMMA_EN));
        }
        List<DeviceGroupTreeHierarchyVO> originAllDeviceGroupData = deviceGroupMapper.listAllDeviceGroupTree(resolvingPowers);
        // 按照中文名称正序排序
        originAllDeviceGroupData = ListSortUtils.sortDeviceGroupTreeByName(originAllDeviceGroupData);
        // 超管用户直接获取完整数据
        if ( UserContextHolder.currentUser().isSuper() ) {
            return TreeUtils.getTreeList(originAllDeviceGroupData);
        }

        // --- 非超级管理员需要过滤并获取当前用户角色所操作的终端分组
        List<DeviceGroupTreeHierarchyVO> userData = new ArrayList<>();
        List<DeviceGroupTreeHierarchyVO> userDeviceGroupTree = getUserDeviceGroupTree(originAllDeviceGroupData);
        if ( userDeviceGroupTree != null && !userDeviceGroupTree.isEmpty() ) {

            // 如果当前登录用户的分组树的根节点ID不是系统默认的ID,则自行创建一个“根节点”便于前端展示
            if ( !DeviceGroupConstant.ALL_DEVICE_GROUP_ID.equals(userDeviceGroupTree.get(0).getId()) ) {
                DeviceGroupTreeHierarchyVO root = new DeviceGroupTreeHierarchyVO();
                root.setParentId(null);
                root.setId(null);
                root.setName("全部分组");
                root.setDeviceCount(0);
                root.setChildren(userDeviceGroupTree);
                userData.add(root);
            } else {
                userData = userDeviceGroupTree;
            }
        }

        return userData;

    }

    private List<DeviceGroupTreeHierarchyVO> getUserDeviceGroupTree(List<DeviceGroupTreeHierarchyVO> originAllDeviceGroupData) {

        // 获取当前用户可以操作的终端分组id
        List<Long> userDeviceGroupIds = deviceGroupMapper.getDeviceGroupIdsByUserId(UserContextHolder.currentUser().getUserId());

        if (userDeviceGroupIds == null || userDeviceGroupIds.isEmpty()) {
            return null;
        }

        // 原始完整分组转换为树形结构
        List<DeviceGroupTreeHierarchyVO> treeData = TreeUtils.getTreeList(originAllDeviceGroupData);

        // 如果用户拥有的分组id包含“所有分组”返回整棵树
        if (userDeviceGroupIds.contains(DeviceGroupConstant.ALL_DEVICE_GROUP_ID)){
           return  treeData;
        }

        // 当前用户的分组
        List<DeviceGroupTreeHierarchyVO> userData = new ArrayList<>();

        // 递归循环遍历原始树形分组，比对并设置当前用户的分组
        getRecursion(treeData, userDeviceGroupIds, userData);

        // 对最终的用户树去重 ==>> 两两循环比对
        Iterator<DeviceGroupTreeHierarchyVO> iteratorInner = userData.iterator();
        while (iteratorInner.hasNext()) {
            DeviceGroupTreeHierarchyVO inner = iteratorInner.next();
            Iterator<DeviceGroupTreeHierarchyVO> iteratorOuter = new ArrayList<>(userData).iterator();
            while (iteratorOuter.hasNext()) {
                DeviceGroupTreeHierarchyVO outer = iteratorOuter.next();
                if (isSubTree(inner,outer)){
                    // 去重
                    iteratorInner.remove();
                    iteratorOuter.remove();
                }
            }
        }
        return userData;
    }


    @Override
    public List<String> findUserDeviceGroupId() {
        Set<String> ids = new HashSet<>();
        // 获取基础终端分组完整数据
        List<DeviceGroupTreeHierarchyVO> originData = deviceGroupMapper.listAllDeviceGroupTree(null);

        // 超管用户直接获取完整数据
        if (UserContextHolder.currentUser().isSuper()) {
            for (DeviceGroupTreeHierarchyVO ele : originData) {
                ids.add(ele.getId().toString());
            }
            return new ArrayList<>(ids);
        }

        // --- // 非超级管理员需要过滤并获取当前用户角色所操作的终端分组
        List<Long> userDeviceGroupIds = deviceGroupMapper.getDeviceGroupIdsByUserId(UserContextHolder.currentUser().getUserId());
        if (userDeviceGroupIds == null || userDeviceGroupIds.isEmpty()) {
            return null;
        }

        // --- 非超级管理员需要过滤并获取当前用户角色所操作的终端分组
        List<DeviceGroupTreeHierarchyVO> userDeviceGroupTree = getUserDeviceGroupTree(originData);

        return getDeviceGroupId(userDeviceGroupTree, ids);
    }

    /**
     * // treeData 是完整的原始分组树形结构，根据用户分配的分组id权限，和原始树形结构，构建一颗属于用户的树
     * //  递归遍历步骤：
     * -- 》 1、遍历整原始分组树的根节点。
     * - -》 2、遍历用户分配的分组id集合。
     * -- 》 3、当前遍历的原始分组树节点与用户任意分配的分组id相等，即为匹配成功。
     * -- 》 4、匹配成功：移除当前用户分配的分组id。
     * -- 》 5、匹配不成功：遍历原始分组树的根节点下的下一节点（重复1、2、3、4步骤）。
     * -- 》 6、退出条件：当用户分组集合为空时，不在继续遍历。
     *
     * @param treeData
     * @param userDeviceGroupIds
     * @param userData
     * @return
     */
    private void getRecursion(List<DeviceGroupTreeHierarchyVO> treeData, List<Long> userDeviceGroupIds, List<DeviceGroupTreeHierarchyVO> userData) {

        // 退出条件：用户分组id集合为空
        boolean finished = userDeviceGroupIds.isEmpty();
        for (DeviceGroupTreeHierarchyVO ele : treeData) {
            if (finished) {
                break;
            }
            Iterator groupId = userDeviceGroupIds.iterator();
            while (groupId.hasNext()) {
                Long next = (Long) groupId.next();
                if (ele.getId().equals(next)) {
                    // 匹配成功一个则移除该id
                    groupId.remove();
                    // 将该id对应的树保存下来（在多次循环过程中可能会出现重复数据：如 1,3,5、3,5 其中3,5的子树是重复的）
                    userData.add(ele);
                }
            }
            if (ele.getChildren() != null && !ele.getChildren().isEmpty()) {
                getRecursion(ele.getChildren(), userDeviceGroupIds, userData);
            }
        }
    }

    /**
     * 判断前者是否是后者的子树
     * @param tree2
     * @param tree1
     * @return
     */
    private boolean isSubTree(DeviceGroupTreeHierarchyVO tree2, DeviceGroupTreeHierarchyVO tree1) {
        // 如果tree2的父id等于tree1的id则 tree2是tree1的子集
        if (tree2.getParentId().equals(tree1.getId().toString())){
            return true;
        }
        // 递归循环比对是否为其子节点
        return recursionCheckIsSub(tree2,tree1.getChildren());

    }

    private boolean recursionCheckIsSub(DeviceGroupTreeHierarchyVO tree2, List<DeviceGroupTreeHierarchyVO> children) {
        // 定义最终的比对结果
        boolean flag = false;

        // 无子节点集合。则tree2也不可能是他们的子节点
        if (CollectionUtils.isEmpty(children)){
            return false;
        }
        for (DeviceGroupTreeHierarchyVO child : children){
            if ( tree2.getParentId().equals(child.getId().toString()) ) {
                // 比对成功终止循环
                flag = true;
                break;
            } else {
                flag = recursionCheckIsSub(tree2,child.getChildren()) ;
                // 比对成功终止循环
                if ( flag ) {
                    break;
                }
            }
        }
        return flag;
    }

    private List<String> getDeviceGroupId(List<DeviceGroupTreeHierarchyVO> userData, Set<String> ids) {
        for (DeviceGroupTreeHierarchyVO ele : userData) {
            if (ele.getChildren() != null) {
                getDeviceGroupId(ele.getChildren(), ids);
            }
            ids.add(ele.getId().toString());
        }
        return new ArrayList<>(ids);
    }

    /**
     * 移动终端到分组
     *
     * @param deviceGroupId 分组ID
     * @param deviceCodes   终端唯一编码，多个用逗号分隔
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OperationResult moveDeviceGroup(Long deviceGroupId, String deviceCodes) {
        if (deviceGroupId == null || StringUtils.isBlank(deviceCodes)) {
            return OperationResult.newValidateFailOperationResult("移动分组信息不能为空");
        }
        DeviceGroup deviceGroup = deviceGroupMapper.getDeviceGroupById(deviceGroupId);
        if (deviceGroup == null) {
            return OperationResult.newValidateFailOperationResult("移动分组信息不存在");
        }
        deviceGroupMapper.moveDeviceGroup(deviceGroupId, Arrays.asList(deviceCodes.split(",")));
        return OperationResult.newSuccessOperationResult();
    }

    @Override
    public List<TreeHierarchyVO> deviceGroupTreeByRole(Long roleId) {
        if (roleId == null) {
            return null;
        }
        // 获取基础终端分组完整数据
        List<DeviceGroupTreeHierarchyVO> originData = deviceGroupMapper.listAllDeviceGroupTree(null);

        // 获取当前角色可以操作的终端分组id
        List<Long> roleDeviceGroupIds = deviceGroupMapper.getDeviceGroupIdsByRoleId(roleId);

        if (roleDeviceGroupIds == null || roleDeviceGroupIds.isEmpty()) {
            // 原始完整分组转换为树形结构
            return TreeUtils.getTreeList(originData);

        }

        setChecked(originData, roleDeviceGroupIds);

        // 原始完整分组转换为树形结构
        List<TreeHierarchyVO> treeData = TreeUtils.getTreeList(originData);
        return treeData;
    }

    private void setChecked(List<DeviceGroupTreeHierarchyVO> originData, List<Long> roleDeviceGroupIds) {
        for (DeviceGroupTreeHierarchyVO tree : originData) {
            for (Long id : roleDeviceGroupIds) {
                if (tree.getId().equals(id)) {
                    tree.setChecked(Boolean.TRUE);
                }
            }
        }

    }
}
